/*
 * package.cpp
 *
 *  Created on: Jan 17, 2016
 *      Author: root
 */
#include<iostream>
#include<string>
#include<string.h>
#include<sstream>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netinet/tcp.h>
#include<netinet/ip.h>
#include<netdb.h>
#include<errno.h>
#include<stdlib.h>

#include "proxy.h"
using namespace dounine_proxy;

using namespace std;

namespace dounine_package {

	string uint_to_ip(unsigned int ip){
		ostringstream ii;
		ii<<((ip>>0)&0xFF);
		ii<<".";
		ii<<((ip>>8)&0xFF);
		ii<<".";
		ii<<((ip>>16)&0xFF);
		ii<<".";
		ii<<((ip>>24)&0xFF);
		return ii.str();
	}

	bool process_packet(unsigned char* buffer, int size){
		//Get the IP Header part of this packet
		struct iphdr *iph = (struct iphdr*)buffer;
		struct sockaddr_in source,dest;
		unsigned short iphdrlen;
		if(iph->protocol == 6){
			struct iphdr *iph = (struct iphdr *)buffer;

			iphdrlen = iph->ihl*4;

			struct tcphdr *tcph=(struct tcphdr*)(buffer + iphdrlen);


			memset(&source, 0, sizeof(source));
			source.sin_addr.s_addr = iph->saddr;

			memset(&dest, 0, sizeof(dest));
			dest.sin_addr.s_addr = iph->daddr;
			if(tcph->syn == 1 && tcph->ack == 1){//&&source.sin_addr.s_addr == dest_ip.s_addr
				cout<<uint_to_ip(iph->saddr)<<"port "<<ntohs(tcph->source)<<" open "<<endl;
				dounine_proxy::check_proxy(uint_to_ip(iph->saddr),ntohs(tcph->source));//validator ip:port proxy
				return true;
			}
		}
		return false;
	}

	unsigned short csum(unsigned short *ptr,int nbytes) {
		register long sum;
		unsigned short oddbyte;
		register short answer;

		sum=0;
		while(nbytes>1) {
			sum+=*ptr++;
			nbytes-=2;
		}
		if(nbytes==1) {
			oddbyte=0;
			*((u_char*)&oddbyte)=*(u_char*)ptr;
			sum+=oddbyte;
		}

		sum = (sum>>16)+(sum & 0xffff);
		sum = sum + (sum>>16);
		answer=(short)~sum;

		return(answer);
	}

	char* hostname_to_ip(char * hostname){
		struct hostent *he;
		struct in_addr **addr_list;
		int i;

		if ( (he = gethostbyname( hostname ) ) == NULL) {
			// get the host info
			herror("gethostbyname");
			return NULL;
		}

		addr_list = (struct in_addr **) he->h_addr_list;

		for(i = 0; addr_list[i] != NULL; i++) {
			//Return the first one;
			return inet_ntoa(*addr_list[i]) ;
		}

		return NULL;
	}

	void get_local_ip ( char * buffer){
		int sock = socket ( AF_INET, SOCK_DGRAM, 0);

		const char* kGoogleDnsIp = "8.8.8.8";
		int dns_port = 53;

		struct sockaddr_in serv;

		memset( &serv, 0, sizeof(serv) );
		serv.sin_family = AF_INET;
		serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
		serv.sin_port = htons( dns_port );

		int err = connect( sock , (const struct sockaddr*) &serv , sizeof(serv) );

		struct sockaddr_in name;
		socklen_t namelen = sizeof(name);
		err = getsockname(sock, (struct sockaddr*) &name, &namelen);

		const char *p = inet_ntop(AF_INET, &name.sin_addr, buffer, 100);

		close(sock);
	}

	void read_syn_ack (){
		int sock_raw;
		int data_size;
		socklen_t saddr_size;
		struct sockaddr saddr;

		unsigned char *buffer = (unsigned char *)malloc(65536); //Its Big!

		cout<<"capture run..."<<endl;

		//Create a raw socket that shall sniff
		sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);

		saddr_size = sizeof(saddr);

		//struct timeval tv;
		//tv.tv_sec = SYN_TIMEOUT;//timeout seconds
		//tv.tv_usec = 0;//us
		//setsockopt(sock_raw,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));//rece package timeout
		while(true){
			//接收包
			data_size = recvfrom(sock_raw , buffer , 65536 , 0 , &saddr , &saddr_size);

			if(data_size > 0 ){
				dounine_package::process_packet(buffer , data_size);
			}

			free(buffer);
			buffer = (unsigned char *)malloc(65536);
		}
		close(sock_raw);
	}

	struct pseudo_header    //needed for checksum calculation
	{
		unsigned int source_address;
		unsigned int dest_address;
		unsigned char placeholder;
		unsigned char protocol;
		unsigned short tcp_length;

		struct tcphdr tcp;
	};

	int syn_sock = socket (AF_INET, SOCK_RAW , IPPROTO_TCP);
	void send_syn_package(const string& ip,const string& source_ip,const int port){

		//Datagram to represent the packet
		char datagram[4096];

		//IP头
		struct iphdr *iph = (struct iphdr *) datagram;

		//TCP头
		struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip));

		struct sockaddr_in  dest;
		struct pseudo_header psh;
		char *target = (char*)ip.c_str();
		struct in_addr dest_ip;
		dest_ip.s_addr = inet_addr( target );

		int source_port = 43591;

		memset (datagram, 0, sizeof(datagram));	/* zero out the buffer */

		//填充IP头部信息
		iph->ihl = 5;
		iph->version = 4;
		iph->tos = 0;
		iph->tot_len = sizeof (struct ip) + sizeof (struct tcphdr);
		iph->id = htons (54321);	//Id of this packet
		iph->frag_off = htons(16384);
		iph->ttl = 64;
		iph->protocol = IPPROTO_TCP;
		iph->saddr = inet_addr ( source_ip.c_str() );	//Spoof the source ip address
		iph->daddr = dest_ip.s_addr;

		iph->check = dounine_package::csum ((unsigned short *) datagram, iph->tot_len >> 1);

		//填充TCP头部信息
		tcph->source = htons ( source_port );
		tcph->dest = htons (port);
		tcph->seq = htonl(1);
		tcph->ack_seq = 0;
		tcph->doff = sizeof(struct tcphdr) / 4;		//Size of tcp header
		tcph->fin=0;
		tcph->syn=1;
		tcph->rst=0;
		tcph->psh=0;
		tcph->ack=0;
		tcph->urg=0;
		tcph->window = htons ( 14600 );	// maximum allowed window size
		tcph->urg_ptr = 0;

		dest.sin_family = AF_INET;
		dest.sin_addr.s_addr = dest_ip.s_addr;

		psh.source_address = inet_addr( source_ip.c_str() );
		psh.dest_address = dest.sin_addr.s_addr;
		psh.placeholder = 0;
		psh.protocol = IPPROTO_TCP;
		psh.tcp_length = htons( sizeof(struct tcphdr) );

		memcpy(&psh.tcp , tcph , sizeof (struct tcphdr));

		tcph->check = csum( (unsigned short*) &psh , sizeof (struct pseudo_header));

		struct timeval tv;
		tv.tv_sec = 1;
		tv.tv_usec = 0;
		setsockopt (syn_sock, IPPROTO_IP, IP_HDRINCL, &tv, sizeof(tv));

		//Send the packet
		if ( sendto (syn_sock, datagram , sizeof(struct iphdr) + sizeof(struct tcphdr) , 0 , (struct sockaddr *) &dest, sizeof (dest)) < 0){
			cout<<endl<<"send syn failure msg:"<<strerror(errno)<<endl;
		}
	}
}
